package io.vec.demo.mediacodec;

import java.io.IOException;
import java.nio.ByteBuffer;

import android.app.Activity;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex;
import com.google.android.gms.common.api.GoogleApiClient;

public class DecodeActivity extends Activity implements SurfaceHolder.Callback {
	//private static final String SAMPLE = "/sdcard/oculus/360Videos/[Samsung] 360 video demo.mp4";
	//private static final String SAMPLE = "/sdcard/oculus/Movies/Trailers/HenryShort.mp4";
	//private static final String SAMPLE = "/sdcard/oculus/test_2352x1464_3.ts";
	private static final String SAMPLE = "/sdcard/oculus/1080p.ts";
	//private static final String SAMPLE = "/sdcard/oculus/720p.ts";
	private PlayerThread mPlayer = null;
	/**
	 * ATTENTION: This was auto-generated to implement the App Indexing API.
	 * See https://g.co/AppIndexing/AndroidStudio for more information.
	 */
	private GoogleApiClient client;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		SurfaceView sv = new SurfaceView(this);
		sv.getHolder().addCallback(this);
		setContentView(sv);
		// ATTENTION: This was auto-generated to implement the App Indexing API.
		// See https://g.co/AppIndexing/AndroidStudio for more information.
		client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
	}

	protected void onDestroy() {
		super.onDestroy();
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		if (mPlayer == null) {
			mPlayer = new PlayerThread(holder.getSurface());
			mPlayer.start();
		}
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		if (mPlayer != null) {
			mPlayer.interrupt();
		}
	}

	@Override
	public void onStart() {
		super.onStart();

		// ATTENTION: This was auto-generated to implement the App Indexing API.
		// See https://g.co/AppIndexing/AndroidStudio for more information.
		client.connect();
		Action viewAction = Action.newAction(
				Action.TYPE_VIEW, // TODO: choose an action type.
				"Decode Page", // TODO: Define a title for the content shown.
				// TODO: If you have web page content that matches this app activity's content,
				// make sure this auto-generated web page URL is correct.
				// Otherwise, set the URL to null.
				Uri.parse("http://host/path"),
				// TODO: Make sure this auto-generated app deep link URI is correct.
				Uri.parse("android-app://io.vec.demo.mediacodec/http/host/path")
		);
		AppIndex.AppIndexApi.start(client, viewAction);
	}

	@Override
	public void onStop() {
		super.onStop();

		// ATTENTION: This was auto-generated to implement the App Indexing API.
		// See https://g.co/AppIndexing/AndroidStudio for more information.
		Action viewAction = Action.newAction(
				Action.TYPE_VIEW, // TODO: choose an action type.
				"Decode Page", // TODO: Define a title for the content shown.
				// TODO: If you have web page content that matches this app activity's content,
				// make sure this auto-generated web page URL is correct.
				// Otherwise, set the URL to null.
				Uri.parse("http://host/path"),
				// TODO: Make sure this auto-generated app deep link URI is correct.
				Uri.parse("android-app://io.vec.demo.mediacodec/http/host/path")
		);
		AppIndex.AppIndexApi.end(client, viewAction);
		client.disconnect();
	}

	private class PlayerThread extends Thread {
		private MediaExtractor extractor;
		private MediaCodec decoder;
		private Surface surface;

		public PlayerThread(Surface surface) {
			this.surface = surface;
		}

		@Override
		public void run() {
			extractor = new MediaExtractor();
			try {
				extractor.setDataSource(SAMPLE);
			} catch (IOException e) {
				e.printStackTrace();
			}

			for (int i = 0; i < extractor.getTrackCount(); i++) {
				MediaFormat format = extractor.getTrackFormat(i);
				String mime = format.getString(MediaFormat.KEY_MIME);
				if (mime.startsWith("video/")) {
					extractor.selectTrack(i);
					decoder = MediaCodec.createDecoderByType(mime);
					decoder.configure(format, surface, null, 0);
					break;
				}
			}

			if (decoder == null) {
				Log.e("DecodeActivity", "Can't find video info!");
				return;
			}

			decoder.start();

			ByteBuffer[] inputBuffers = decoder.getInputBuffers();
			ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
			BufferInfo info = new BufferInfo();
			boolean isEOS = false;
			long startMs = System.currentTimeMillis();
			int n = 0,m = 0;

			int ipframe_nums[] = {
					0,1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,34,37,40,43,44,45,48,51,54,57,
					60,63,66,69,72,75,78,81,84,87,88,89,92,95,98,101,104,107,110,113,116,119,122
			};
			int frame_num = 100;
			while (!Thread.interrupted()) {
				//if (!isEOS) {
				if (n < frame_num) {
					int inIndex = decoder.dequeueInputBuffer(1000);
					if (inIndex >= 0) {
						ByteBuffer buffer = inputBuffers[inIndex];
						int sampleSize = extractor.readSampleData(buffer, 0);

						if (sampleSize < 0) {
							// We shouldn't stop the playback at this point, just pass the EOS
							// flag to decoder, we will get it again from the
							// dequeueOutputBuffer
							Log.d("DecodeActivity", "InputBuffer BUFFER_FLAG_END_OF_STREAM");
							decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
							isEOS = true;
						} else {
							Log.d("mediacodecDump", "input " + n + " " + sampleSize);
							decoder.queueInputBuffer(inIndex, 0, sampleSize, n/*extractor.getSampleTime()*/, 0);
							extractor.advance();
							n += 1;
						}
					}
					//if ( false ) {
					if (m != n) {
						m = n;
						inIndex = decoder.dequeueInputBuffer(10000);
						if (inIndex >= 0) {
							ByteBuffer buffer = inputBuffers[inIndex];
							byte[] sp_end = {
									//0x00, 0x00, 0x41, 0x01, (byte) 0x9A, 0x04, 0x08, 0x22, 0x00
									0x00, 0x00, 0x01, 0x41, (byte) 0x9A, 0x04, 0x08, 0x44, (byte) 0x3f
									//0x00, 0x00, 0x01, 0x41, (byte) 0x9A, 0x04, 0x08, 0x02, (byte) 0xB4
									//0x00, 0x00, 0x01, 0x09, (byte) 0x9A, 0x04, 0x08, 0x02, (byte) 0xB4, 0x00, 0x69, 0x18, 0x00, 'S', 'P', 'E', 'C', 'I', 'A', 'L', 'F', 'R', 'A', 'M', 'E', '\0'
							};
							int sampleSize = sp_end.length;
							buffer.put(sp_end, 0, sampleSize);
							Log.d("mediacodecDump", "input sp ");
							decoder.queueInputBuffer(inIndex, 0, sampleSize, 12345, 0);
						}
					}
				}

				//boolean gotdata = true;
				boolean gotdata = false;
				do {
					int outIndex = decoder.dequeueOutputBuffer(info, 1000);
					switch (outIndex) {
						case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
							Log.d("mediacodecDump", "INFO_OUTPUT_BUFFERS_CHANGED");
							outputBuffers = decoder.getOutputBuffers();
							break;
						case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
							Log.d("mediacodecDump", "New format " + decoder.getOutputFormat());
							break;
						case MediaCodec.INFO_TRY_AGAIN_LATER:
							Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
							break;
						default:
							ByteBuffer buffer = outputBuffers[outIndex];
							//Log.v("DecodeActivity", "We can't use this buffer but render it due to the API limit, " + buffer);
							//Log.d("mediacodecDump", "output " + info.presentationTimeUs);

							if (info.presentationTimeUs != 12345) {
								decoder.releaseOutputBuffer(outIndex, true);
								Log.d("mediacodecDump", "output " + info.presentationTimeUs);
								gotdata = true;
							}
							else {
								decoder.releaseOutputBuffer(outIndex, false);
							}
							break;
					}
				}while (!gotdata);
				if (n >= frame_num) {
					Log.d("mediacodecDump", "totle times " + (System.currentTimeMillis() - startMs));
					Log.d("mediacodecDump", "average time " + ((System.currentTimeMillis() - startMs)/frame_num));
					break;
				}

				/*try {
					sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}*/


				// All decoded frames have been rendered, we can stop playing now
				if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
					Log.d("DecodeActivity", "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
					break;
				}
			}

			decoder.stop();
			decoder.release();
			extractor.release();
		}
	}
}